home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 176-200 / disk_195 / microemacs / src.zoo / search.c < prev    next >
C/C++ Source or Header  |  1989-03-23  |  41KB  |  1,761 lines

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions.
  4.  *
  5.  * Aug. 1986 John M. Gamble:
  6.  *    Made forward and reverse search use the same scan routine.
  7.  *
  8.  *    Added a limited number of regular expressions - 'any',
  9.  *    'character class', 'closure', 'beginning of line', and
  10.  *    'end of line'.
  11.  *
  12.  *    Replacement metacharacters will have to wait for a re-write of
  13.  *    the replaces function, and a new variation of ldelete().
  14.  *
  15.  *    For those curious as to my references, i made use of
  16.  *    Kernighan & Plauger's "Software Tools."
  17.  *    I deliberately did not look at any published grep or editor
  18.  *    source (aside from this one) for inspiration.  I did make use of
  19.  *    Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
  20.  *    June, 1985 and modified them for the limited needs of character class
  21.  *    matching.  Any inefficiences, bugs, stupid coding examples, etc.,
  22.  *    are therefore my own responsibility.
  23.  *
  24.  * April 1987: John M. Gamble
  25.  *    Deleted the "if (n == 0) n = 1;" statements in front of the
  26.  *    search/hunt routines.  Since we now use a do loop, these
  27.  *    checks are unnecessary.  Consolidated common code into the
  28.  *    function delins().  Renamed global mclen matchlen,
  29.  *    and added the globals matchline, matchoff, patmatch, and
  30.  *    mlenold.
  31.  *    This gave us the ability to unreplace regular expression searches,
  32.  *    and to put the matched string into an evironment variable.
  33.  *    SOON TO COME: Meta-replacement characters!
  34.  *
  35.  *    25-apr-87    DML
  36.  *    - cleaned up an unneccessary if/else in forwsearch() and
  37.  *      backsearch()
  38.  *    - savematch() failed to malloc room for the terminating byte
  39.  *      of the match string (stomp...stomp...). It does now. Also
  40.  *      it now returns gracefully if malloc fails
  41.  *
  42.  *    July 1987: John M. Gamble
  43.  *    Set the variables matchlen and matchoff in the 'unreplace'
  44.  *    section of replaces().  The function savematch() would
  45.  *    get confused if you replaced, unreplaced, then replaced
  46.  *    again (serves you right for being so wishy-washy...)
  47.  *
  48.  *    August 1987: John M. Gamble
  49.  *    Put in new function rmcstr() to create the replacement
  50.  *    meta-character array.  Modified delins() so that it knows
  51.  *    whether or not to make use of the array.  And, put in the
  52.  *    appropriate new structures and variables.
  53.  *
  54.  *    4 November 1987    Geoff Gibbs
  55.  *
  56.  *    Fast version using simplified version of Boyer and Moore
  57.  *    Software-Practice and Experience, vol 10, 501-506 (1980).
  58.  *    Mods to scanner() and readpattern(), and added fbound() and
  59.  *    setjtable().  Scanner() should be callable as before, provided
  60.  *    setjtable() has been called first.
  61.  *
  62.  *    December 1987: John M. Gamble
  63.  *    Made replaces() beep at you and stop if an empty string is
  64.  *    replaced.  This is possible in MAGIC mode, if you pick your
  65.  *    pattern incorrectly.
  66.  *    Fixed a subtle bug in the new Boyer-Moore routines, which caused
  67.  *    searches to fail occasionally.
  68.  *
  69.  *    12-apr-88 Daniel Lawrence
  70.  *    Fixed a bug in the replaces() which would misplace the point after
  71.  *    aborting a query-replace-string.
  72.  */
  73.  
  74. #include        <stdio.h>
  75. #include    "estruct.h"
  76. #include    "etype.h"
  77. #include        "edef.h"
  78. #include    "elang.h"
  79.  
  80. static int patlenadd;
  81. static int deltaf[HICHAR], deltab[HICHAR];
  82. static int lastchfjump, lastchbjump;
  83.  
  84. /*
  85.  * forwsearch -- Search forward.  Get a search string from the user, and
  86.  *    search for the string.  If found, reset the "." to be just after
  87.  *    the match string, and (perhaps) repaint the display.
  88.  */
  89. PASCAL NEAR forwsearch(f, n)
  90. int f, n;    /* default flag / numeric argument */
  91. {
  92.     register int status = TRUE;
  93.  
  94.     /* If n is negative, search backwards.
  95.      * Otherwise proceed by asking for the search string.
  96.      */
  97.     if (n < 0)
  98.         return(backsearch(f, -n));
  99.  
  100.     /* Ask the user for the text of a pattern.  If the
  101.      * response is TRUE (responses other than FALSE are
  102.      * possible), search for the pattern for as long as
  103.      * n is positive (n == 0 will go through once, which
  104.      * is just fine).
  105.      */
  106.     if ((status = readpattern(TEXT78, &pat[0], TRUE)) == TRUE) {
  107. /*                                "Search" */
  108.         do {
  109. #if    MAGIC
  110.             if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  111.                 status = mcscanner(&mcpat[0], FORWARD, PTEND);
  112.             else
  113. #endif
  114.                 status = scanner(&pat[0], FORWARD, PTEND);
  115.         } while ((--n > 0) && status);
  116.  
  117.         /* Save away the match, or complain
  118.          * if not there.
  119.          */
  120.         if (status != TRUE)
  121.             mlwrite(TEXT79);
  122. /*                              "Not found" */
  123.     }
  124.     return(status);
  125. }
  126.  
  127. /*
  128.  * forwhunt -- Search forward for a previously acquired search string.
  129.  *    If found, reset the "." to be just after the match string,
  130.  *    and (perhaps) repaint the display.
  131.  */
  132.  
  133. PASCAL NEAR forwhunt(f, n)
  134. int f, n;    /* default flag / numeric argument */
  135. {
  136.     register int status = TRUE;
  137.  
  138.     if (n < 0)        /* search backwards */
  139.         return(backhunt(f, -n));
  140.  
  141.     /* Make sure a pattern exists, or that we didn't switch
  142.      * into MAGIC mode until after we entered the pattern.
  143.      */
  144.     if (pat[0] == '\0')
  145.     {
  146.         mlwrite(TEXT80);
  147. /*                      "No pattern set" */
  148.         return FALSE;
  149.     }
  150. #if    MAGIC
  151.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  152.          mcpat[0].mc_type == MCNIL)
  153.     {
  154.         if (!mcstr())
  155.             return FALSE;
  156.     }
  157. #endif
  158.  
  159.     /* Search for the pattern for as long as
  160.      * n is positive (n == 0 will go through once, which
  161.      * is just fine).
  162.      */
  163.     do
  164.     {
  165. #if    MAGIC
  166.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  167.             status = mcscanner(&mcpat[0], FORWARD, PTEND);
  168.         else
  169. #endif
  170.             status = scanner(&pat[0], FORWARD, PTEND);
  171.     } while ((--n > 0) && status);
  172.  
  173.     /* Save away the match, or complain
  174.      * if not there.
  175.      */
  176.     if (status != TRUE)
  177.         mlwrite(TEXT79);
  178. /*                      "Not found" */
  179.  
  180.     return(status);
  181. }
  182.  
  183. /*
  184.  * backsearch -- Reverse search.  Get a search string from the user, and
  185.  *    search, starting at "." and proceeding toward the front of the buffer.
  186.  *    If found "." is left pointing at the first character of the pattern
  187.  *    (the last character that was matched).
  188.  */
  189. PASCAL NEAR backsearch(f, n)
  190. int f, n;    /* default flag / numeric argument */
  191. {
  192.     register int status = TRUE;
  193.  
  194.     /* If n is negative, search forwards.
  195.      * Otherwise proceed by asking for the search string.
  196.      */
  197.     if (n < 0)
  198.         return(forwsearch(f, -n));
  199.  
  200.     /* Ask the user for the text of a pattern.  If the
  201.      * response is TRUE (responses other than FALSE are
  202.      * possible), search for the pattern for as long as
  203.      * n is positive (n == 0 will go through once, which
  204.      * is just fine).
  205.      */
  206.     if ((status = readpattern(TEXT81, &pat[0], TRUE)) == TRUE) {
  207. /*                                "Reverse search" */
  208.         do {
  209. #if    MAGIC
  210.             if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  211.                 status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  212.             else
  213. #endif
  214.                 status = scanner(&tap[0], REVERSE, PTBEG);
  215.         } while ((--n > 0) && status);
  216.  
  217.         /* Save away the match, or complain
  218.          * if not there.
  219.          */
  220.         if (status != TRUE)
  221.             mlwrite(TEXT79);
  222. /*                              "Not found" */
  223.     }
  224.     return(status);
  225. }
  226.  
  227. /*
  228.  * backhunt -- Reverse search for a previously acquired search string,
  229.  *    starting at "." and proceeding toward the front of the buffer.
  230.  *    If found "." is left pointing at the first character of the pattern
  231.  *    (the last character that was matched).
  232.  */
  233. PASCAL NEAR backhunt(f, n)
  234. int f, n;    /* default flag / numeric argument */
  235. {
  236.     register int    status = TRUE;
  237.  
  238.     if (n < 0)
  239.         return(forwhunt(f, -n));
  240.  
  241.     /* Make sure a pattern exists, or that we didn't switch
  242.      * into MAGIC mode until after we entered the pattern.
  243.      */
  244.     if (tap[0] == '\0')
  245.     {
  246.         mlwrite(TEXT80);
  247. /*                      "No pattern set" */
  248.         return FALSE;
  249.     }
  250. #if    MAGIC
  251.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  252.          tapcm[0].mc_type == MCNIL)
  253.     {
  254.         if (!mcstr())
  255.             return FALSE;
  256.     }
  257. #endif
  258.  
  259.     /* Go search for it for as long as
  260.      * n is positive (n == 0 will go through once, which
  261.      * is just fine).
  262.      */
  263.     do
  264.     {
  265. #if    MAGIC
  266.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  267.             status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  268.         else
  269. #endif
  270.             status = scanner(&tap[0], REVERSE, PTBEG);
  271.     } while ((--n > 0) && status);
  272.  
  273.     /* Save away the match, or complain
  274.      * if not there.
  275.      */
  276.     if (status != TRUE)
  277.         mlwrite(TEXT79);
  278. /*                      "Not found" */
  279.  
  280.     return(status);
  281. }
  282.  
  283. #if    MAGIC
  284. /*
  285.  * mcscanner -- Search for a meta-pattern in either direction.  If found,
  286.  *    reset the "." to be at the start or just after the match string,
  287.  *    and (perhaps) repaint the display.
  288.  */
  289. int PASCAL NEAR mcscanner(mcpatrn, direct, beg_or_end)
  290. MC    *mcpatrn;    /* pointer into pattern */
  291. int    direct;        /* which way to go.*/
  292. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  293. {
  294.     LINE *curline;            /* current line during scan */
  295.     int curoff;            /* position within current line */
  296.  
  297.     /* If we are going in reverse, then the 'end' is actually
  298.      * the beginning of the pattern.  Toggle it.
  299.      */
  300.     beg_or_end ^= direct;
  301.  
  302.     /*
  303.      * Save the old matchlen length, in case it is
  304.      * very different (closure) from the old length.
  305.      * This is important for query-replace undo
  306.      * command.
  307.      */
  308.     mlenold = matchlen;
  309.  
  310.     /* Setup local scan pointers to global ".".
  311.      */
  312.     curline = curwp->w_dotp;
  313.     curoff  = curwp->w_doto;
  314.  
  315.     /* Scan each character until we hit the head link record.
  316.      */
  317.     while (!boundry(curline, curoff, direct))
  318.     {
  319.         /* Save the current position in case we need to
  320.          * restore it on a match, and initialize matchlen to
  321.          * zero in case we are doing a search for replacement.
  322.          */
  323.         matchline = curline;
  324.         matchoff = curoff;
  325.         matchlen = 0;
  326.  
  327.         if (amatch(mcpatrn, direct, &curline, &curoff))
  328.         {
  329.             /* A SUCCESSFULL MATCH!!!
  330.              * reset the global "." pointers.
  331.              */
  332.             if (beg_or_end == PTEND)    /* at end of string */
  333.             {
  334.                 curwp->w_dotp = curline;
  335.                 curwp->w_doto = curoff;
  336.             }
  337.             else        /* at beginning of string */
  338.             {
  339.                 curwp->w_dotp = matchline;
  340.                 curwp->w_doto = matchoff;
  341.             }
  342.  
  343.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  344.             savematch();
  345.             return TRUE;
  346.         }
  347.  
  348.         /* Advance the cursor.
  349.          */
  350.         nextch(&curline, &curoff, direct);
  351.     }
  352.  
  353.     return FALSE;    /* We could not find a match.*/
  354. }
  355.  
  356. /*
  357.  * amatch -- Search for a meta-pattern in either direction.  Based on the
  358.  *    recursive routine amatch() (for "anchored match") in
  359.  *    Kernighan & Plauger's "Software Tools".
  360.  */
  361. int PASCAL NEAR    amatch(mcptr, direct, pcwline, pcwoff)
  362. register MC    *mcptr;    /* string to scan for */
  363. int        direct;        /* which way to go.*/
  364. LINE        **pcwline;    /* current line during scan */
  365. int        *pcwoff;    /* position within current line */
  366. {
  367.     register int c;            /* character at current position */
  368.     LINE *curline;            /* current line during scan */
  369.     int curoff;            /* position within current line */
  370.     int nchars;
  371.  
  372.     /* Set up local scan pointers to ".", and get
  373.      * the current character.  Then loop around
  374.      * the pattern pointer until success or failure.
  375.      */
  376.     curline = *pcwline;
  377.     curoff = *pcwoff;
  378.  
  379.     /* The beginning-of-line and end-of-line metacharacters
  380.      * do not compare against characters, they compare
  381.      * against positions.
  382.      * BOL is guaranteed to be at the start of the pattern
  383.      * for forward searches, and at the end of the pattern
  384.      * for reverse searches.  The reverse is true for EOL.
  385.      * So, for a start, we check for them on entry.
  386.      */
  387.     if (mcptr->mc_type == BOL)
  388.     {
  389.         if (curoff != 0)
  390.             return FALSE;
  391.         mcptr++;
  392.     }
  393.  
  394.     if (mcptr->mc_type == EOL)
  395.     {
  396.         if (curoff != llength(curline))
  397.             return FALSE;
  398.         mcptr++;
  399.     }
  400.  
  401.     while (mcptr->mc_type != MCNIL)
  402.     {
  403.         c = nextch(&curline, &curoff, direct);
  404.  
  405.         if (mcptr->mc_type & CLOSURE)
  406.         {
  407.             /* Try to match as many characters as possible
  408.              * against the current meta-character.  A
  409.              * newline never matches a closure.
  410.              */
  411.             nchars = 0;
  412.             while (c != '\r' && mceq(c, mcptr))
  413.             {
  414.                 c = nextch(&curline, &curoff, direct);
  415.                 nchars++;
  416.             }
  417.  
  418.             /* We are now at the character that made us
  419.              * fail.  Try to match the rest of the pattern.
  420.              * Shrink the closure by one for each failure.
  421.              * Since closure matches *zero* or more occurences
  422.              * of a pattern, a match may start even if the
  423.              * previous loop matched no characters.
  424.              */
  425.             mcptr++;
  426.  
  427.             for (;;)
  428.             {
  429.                 c = nextch(&curline, &curoff, direct ^ REVERSE);
  430.  
  431.                 if (amatch(mcptr, direct, &curline, &curoff))
  432.                 {
  433.                     matchlen += nchars;
  434.                     goto success;
  435.                 }
  436.  
  437.                 if (nchars-- == 0)
  438.                     return FALSE;
  439.             }
  440.         }
  441.         else            /* Not closure.*/
  442.         {
  443.             /* The only way we'd get a BOL metacharacter
  444.              * at this point is at the end of the reversed pattern.
  445.              * The only way we'd get an EOL metacharacter
  446.              * here is at the end of a regular pattern.
  447.              * So if we match one or the other, and are at
  448.              * the appropriate position, we are guaranteed success
  449.              * (since the next pattern character has to be MCNIL).
  450.              * Before we report success, however, we back up by
  451.              * one character, so as to leave the cursor in the
  452.              * correct position.  For example, a search for ")$"
  453.              * will leave the cursor at the end of the line, while
  454.              * a search for ")<NL>" will leave the cursor at the
  455.              * beginning of the next line.  This follows the
  456.              * notion that the meta-character '$' (and likewise
  457.              * '^') match positions, not characters.
  458.              */
  459.             if (mcptr->mc_type == BOL)
  460.                 if (curoff == llength(curline))
  461.                 {
  462.                     nextch(&curline, &curoff,
  463.                            direct ^ REVERSE);
  464.                     goto success;
  465.                 }
  466.                 else
  467.                     return FALSE;
  468.  
  469.             if (mcptr->mc_type == EOL)
  470.                 if (curoff == 0)
  471.                 {
  472.                     nextch(&curline, &curoff,
  473.                            direct ^ REVERSE);
  474.                     goto success;
  475.                 }
  476.                 else
  477.                     return FALSE;
  478.  
  479.             /* Neither BOL nor EOL, so go through
  480.              * the meta-character equal function.
  481.              */
  482.             if (!mceq(c, mcptr))
  483.                 return FALSE;
  484.         }
  485.  
  486.         /* Increment the length counter and
  487.          * advance the pattern pointer.
  488.          */
  489.         matchlen++;
  490.         mcptr++;
  491.     }            /* End of mcptr loop.*/
  492.  
  493.     /* A SUCCESSFULL MATCH!!!
  494.      * Reset the "." pointers.
  495.      */
  496. success:
  497.     *pcwline = curline;
  498.     *pcwoff  = curoff;
  499.  
  500.     return TRUE;
  501. }
  502. #endif
  503.  
  504. /*
  505.  * scanner -- Search for a pattern in either direction.  If found,
  506.  *    reset the "." to be at the start or just after the match string,
  507.  *    and (perhaps) repaint the display.
  508.  *    Fast version using simplified version of Boyer and Moore
  509.  *    Software-Practice and Experience, vol 10, 501-506 (1980)
  510.  */
  511. int PASCAL NEAR scanner(patrn, direct, beg_or_end)
  512. char    *patrn;        /* string to scan for */
  513. int    direct;        /* which way to go.*/
  514. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  515. {
  516.     register int    c;        /* character at current position */
  517.     register char    *patptr;    /* pointer into pattern */
  518.     LINE    *curline;        /* current line during scan */
  519.     int    curoff;            /* position within current line */
  520.     LINE    *scanline;        /* current line during scanning */
  521.     int    scanoff;        /* position in scanned line */
  522.     int    jump;            /* next offset */
  523.  
  524.     /* If we are going in reverse, then the 'end' is actually
  525.      * the beginning of the pattern.  Toggle it.
  526.      */
  527.     beg_or_end ^= direct;
  528.  
  529.     /* Set up local pointers to global ".".
  530.      */
  531.     curline = curwp->w_dotp;
  532.     curoff = curwp->w_doto;
  533.  
  534.     /* Scan each character until we hit the head link record.
  535.      * Get the character resolving newlines, offset
  536.      * by the pattern length, i.e. the last character of the
  537.      * potential match.
  538.      */
  539.     jump = patlenadd;
  540.  
  541.     while (!fbound(jump, &curline, &curoff, direct))
  542.     {
  543.         /* Save the current position in case we match
  544.          * the search string at this point.
  545.          */
  546.         matchline = curline;
  547.         matchoff = curoff;
  548.  
  549.         /* Setup scanning pointers.
  550.          */
  551.         scanline = curline;
  552.         scanoff = curoff;
  553.         patptr = &patrn[0];
  554.  
  555.         /* Scan through the pattern for a match.
  556.          */
  557.         while ((c = *patptr++) != '\0')
  558.             if (!eq((int) c, nextch(&scanline, &scanoff, direct)))
  559.             {
  560.                 jump = (direct == FORWARD)
  561.                     ? lastchfjump
  562.                     : lastchbjump;
  563.                 goto fail;
  564.             }
  565.  
  566.         /* A SUCCESSFULL MATCH!!!
  567.          * reset the global "." pointers
  568.          */
  569.         if (beg_or_end == PTEND)    /* at end of string */
  570.         {
  571.             curwp->w_dotp = scanline;
  572.             curwp->w_doto = scanoff;
  573.         }
  574.         else        /* at beginning of string */
  575.         {
  576.             curwp->w_dotp = matchline;
  577.             curwp->w_doto = matchoff;
  578.         }
  579.  
  580.         curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
  581.         savematch();
  582.         return TRUE;
  583.  
  584. fail:;            /* continue to search */
  585.     }
  586.  
  587.     return FALSE;    /* We could not find a match */
  588. }
  589.  
  590. /*
  591.  * fbound -- Return information depending on whether we have hit a boundry
  592.  *    (and may therefore search no further) or if a trailing character
  593.  *    of the search string has been found.  See boundry() for search
  594.  *    restrictions.
  595.  */
  596. int PASCAL NEAR    fbound(jump, pcurline, pcuroff, dir)
  597. LINE    **pcurline;
  598. int    *pcuroff, dir, jump;
  599. {
  600.     register int spare, curoff;
  601.     register LINE    *curline;
  602.  
  603.     curline = *pcurline;
  604.     curoff = *pcuroff;
  605.  
  606.     if (dir == FORWARD)
  607.     {
  608.         while (jump != 0)
  609.         {
  610.             curoff += jump;
  611.             spare = curoff - llength(curline);
  612.  
  613.             while (spare > 0)
  614.             {
  615.                 curline = lforw(curline);/* skip to next line */
  616.                 curoff = spare - 1;
  617.                 spare = curoff - llength(curline);
  618.                 if (curline == curbp->b_linep)
  619.                     return TRUE;    /* hit end of buffer */
  620.             }
  621.  
  622.             if (spare == 0)
  623.                 jump = deltaf[(int) '\r'];
  624.             else
  625.                 jump = deltaf[(int) lgetc(curline, curoff)];
  626.         }
  627.  
  628.         /* The last character matches, so back up to start
  629.          * of possible match.
  630.          */
  631.         curoff -= patlenadd;
  632.  
  633.         while (curoff < 0)
  634.         {
  635.             curline = lback(curline);/* skip back a line */
  636.             curoff += llength(curline) + 1;
  637.         }
  638.  
  639.     }
  640.     else            /* Reverse.*/
  641.     {
  642.         jump++;        /* allow for offset in reverse */
  643.         while (jump != 0)
  644.         {
  645.             curoff -= jump;
  646.             while (curoff < 0)
  647.             {
  648.                 curline = lback(curline);    /* skip back a line */
  649.                 curoff += llength(curline) + 1;
  650.                 if (curline == curbp->b_linep)
  651.                     return TRUE;    /* hit end of buffer */
  652.             }
  653.  
  654.             if (curoff == llength(curline))
  655.                 jump = deltab[(int) '\r'];
  656.             else
  657.                 jump = deltab[(int) lgetc(curline, curoff)];
  658.         }
  659.  
  660.         /* The last character matches, so back up to start
  661.          * of possible match.
  662.          */
  663.         curoff += matchlen;
  664.         spare = curoff - llength(curline);
  665.         while (spare > 0)
  666.         {
  667.                curline = lforw(curline);/* skip back a line */
  668.                curoff = spare - 1;
  669.             spare = curoff - llength(curline);
  670.         }
  671.     }
  672.  
  673.     *pcurline = curline;
  674.     *pcuroff = curoff;
  675.     return FALSE;
  676. }
  677.  
  678. /*
  679.  * setjtable -- Settting up search delta forward and delta backward
  680.  *    tables.  The reverse search string and string lengths are
  681.  *    set here, for table initialization and for substitution
  682.  *    purposes.  The default for any character to jump is the
  683.  *    pattern length.
  684.  */
  685. PASCAL NEAR setjtable(apat)
  686. char apat[];
  687. {
  688.     int        i;
  689.     
  690.     rvstrcpy(tap, apat);
  691.     patlenadd = (mlenold = matchlen = strlen(apat)) - 1;
  692.  
  693.     for (i = 0; i < HICHAR; i++)
  694.     {
  695.         deltaf[i] = matchlen;
  696.         deltab[i] = matchlen;
  697.     }
  698.  
  699.     /* Now put in the characters contained
  700.      * in the pattern, duplicating the CASE
  701.      */
  702.     for (i = 0; i < patlenadd; i++)
  703.     {
  704. #if    0
  705.         /*
  706.          * Debugging & tracing information.
  707.          */
  708.         mlwrite(TEXT82, (unsigned int) apat[i], patlenadd - i);
  709. /*                      "Considering %d with jump %d" */
  710.         tgetc();
  711.         if (isletter(apat[i]))
  712.         {
  713.             mlwrite(TEXT83, CHCASE((unsigned int) apat[i]));
  714. /*                              "Its other case is %d" */
  715.             tgetc();
  716.         }
  717. #endif
  718.         if (isletter(apat[i]))
  719.             deltaf[(unsigned int) CHCASE((unsigned int) apat[i])]
  720.              = patlenadd - i;
  721.         deltaf[(unsigned int) apat[i]] = patlenadd - i;
  722.  
  723.         if (isletter(tap[i]))
  724.             deltab[(unsigned int) CHCASE((unsigned int) tap[i])]
  725.              = patlenadd - i;
  726.         deltab[(unsigned int) tap[i]] = patlenadd - i;
  727.     }
  728.  
  729.     /* The last character will have the pattern length
  730.      * unless there are duplicates of it.  Get the number to
  731.      * jump from the arrays delta, and overwrite with zeroes in delta
  732.      * duplicating the CASE.
  733.      */
  734.     lastchfjump = patlenadd + deltaf[(unsigned int) apat[patlenadd]];
  735.     lastchbjump = patlenadd + deltab[(unsigned int) apat[0]];
  736.  
  737.     if (isletter(apat[patlenadd]))
  738.         deltaf[(unsigned int) CHCASE(apat[patlenadd])] = 0;
  739.     deltaf[(int) apat[patlenadd]] = 0;
  740.  
  741.     if (isletter(apat[0]))
  742.         deltab[(unsigned int) CHCASE(apat[0])] = 0;
  743.     deltab[(int) apat[0]] = 0;
  744.  
  745. }
  746.  
  747. /*
  748.  * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  749.  *    from the pattern.  If we are not in EXACT mode, fold out the case.
  750.  */
  751. int PASCAL NEAR eq(bc, pc)
  752. register int    bc;
  753. register int    pc;
  754. {
  755.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
  756.     {
  757.         if (islower(bc))
  758.             bc = CHCASE(bc);
  759.  
  760.         if (islower(pc))
  761.             pc = CHCASE(pc);
  762.  
  763.     }
  764.  
  765.     return(bc == pc);
  766. }
  767.  
  768. /*
  769.  * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  770.  *    search string, create the reverse pattern and the magic
  771.  *    pattern, assuming we are in MAGIC mode (and defined that way).
  772.  *    Apat is not updated if the user types in an empty line.  If
  773.  *    the user typed an empty line, and there is no old pattern, it is
  774.  *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  775.  *    There is some do-it-yourself control expansion.  Change to using
  776.  *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  777.  *    string. 
  778.  */
  779. int PASCAL NEAR readpattern(prompt, apat, srch)
  780. char    *prompt;
  781. char    apat[];
  782. int    srch;
  783. {
  784.     int status;
  785.     char tpat[NPAT+20];
  786.  
  787.     strcpy(tpat, prompt);    /* copy prompt to output string */
  788.     strcat(tpat, " [");    /* build new prompt string */
  789.     expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  790.     strcat(tpat, "]<META>: ");
  791.  
  792.     /* Read a pattern.  Either we get one,
  793.      * or we just get the META charater, and use the previous pattern.
  794.      * Then, if it's the search string, make a reversed pattern.
  795.      * *Then*, make the meta-pattern, if we are defined that way.
  796.      */
  797.     if ((status = mltreply(tpat, tpat, NPAT, sterm)) == TRUE)
  798.     {
  799.         strcpy(apat, tpat);
  800.  
  801.         /* If we are doing the search string set the
  802.          * delta tables.
  803.          */
  804.         if (srch)
  805.             setjtable(apat);
  806.  
  807. #if    MAGIC
  808.         /* Only make the meta-pattern if in magic mode,
  809.          * since the pattern in question might have an
  810.          * invalid meta combination.
  811.          */
  812.         if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
  813.         {
  814.             mcclear();
  815.             rmcclear();
  816.         }
  817.         else
  818.             status = srch? mcstr(): rmcstr();
  819. #endif
  820.     }
  821.     else if (status == FALSE && apat[0] != 0)    /* Old one */
  822.         status = TRUE;
  823.  
  824.     return(status);
  825. }
  826.  
  827. /*
  828.  * savematch -- We found the pattern?  Let's save it away.
  829.  */
  830. PASCAL NEAR savematch()
  831. {
  832.     register char    *ptr;        /* pointer to last match string */
  833.     register int    j;
  834.     LINE        *curline;    /* line of last match */
  835.     int        curoff;        /* offset "      "    */
  836.  
  837.     /* Free any existing match string, then
  838.      * attempt to allocate a new one.
  839.      */
  840.     if (patmatch != NULL)
  841.         free(patmatch);
  842.  
  843.     ptr = patmatch = malloc(matchlen + 1);
  844.  
  845.     if (ptr != NULL)
  846.     {
  847.         curoff = matchoff;
  848.         curline = matchline;
  849.  
  850.         for (j = 0; j < matchlen; j++)
  851.             *ptr++ = nextch(&curline, &curoff, FORWARD);
  852.  
  853.         *ptr = '\0';
  854.     }
  855. }
  856.  
  857. /*
  858.  * rvstrcpy -- Reverse string copy.
  859.  */
  860. PASCAL NEAR rvstrcpy(rvstr, str)
  861. register char    *rvstr, *str;
  862. {
  863.     register int i;
  864.  
  865.     str += (i = strlen(str));
  866.  
  867.     while (i-- > 0)
  868.         *rvstr++ = *--str;
  869.  
  870.     *rvstr = '\0';
  871. }
  872.  
  873. /*
  874.  * sreplace -- Search and replace.
  875.  */
  876. PASCAL NEAR sreplace(f, n)
  877. int f;        /* default flag */
  878. int n;        /* # of repetitions wanted */
  879. {
  880.     return(replaces(FALSE, f, n));
  881. }
  882.  
  883. /*
  884.  * qreplace -- search and replace with query.
  885.  */
  886. PASCAL NEAR qreplace(f, n)
  887. int f;        /* default flag */
  888. int n;        /* # of repetitions wanted */
  889. {
  890.     return(replaces(TRUE, f, n));
  891. }
  892.  
  893. /*
  894.  * replaces -- Search for a string and replace it with another
  895.  *    string.  Query might be enabled (according to kind).
  896.  */
  897. int PASCAL NEAR    replaces(kind, f, n)
  898. int    kind;    /* Query enabled flag */
  899. int    f;    /* default flag */
  900. int    n;    /* # of repetitions wanted */
  901. {
  902.     register int status;    /* success flag on pattern inputs */
  903.     register int rlength;    /* length of replacement string */
  904.     register int numsub;    /* number of substitutions */
  905.     register int nummatch;    /* number of found matches */
  906.     int nlflag;        /* last char of search string a <NL>? */
  907.     int nlrepl;        /* was a replace done on the last line? */
  908.     char c;            /* input char for query */
  909.     char tpat[NPAT];    /* temporary to hold search pattern */
  910.     LINE *origline;        /* original "." position */
  911.     int origoff;        /* and offset (for . query option) */
  912.     LINE *lastline;        /* position of last replace and */
  913.     int lastoff;        /* offset (for 'u' query option) */
  914.  
  915.     if (curbp->b_mode & MDVIEW)    /* don't allow this command if    */
  916.         return(rdonly());    /* we are in read only mode    */
  917.  
  918.     /* Check for negative repetitions.
  919.      */
  920.     if (f && n < 0)
  921.         return(FALSE);
  922.  
  923.     /* Ask the user for the text of a pattern.
  924.      */
  925.     if ((status = readpattern(
  926.         (kind == FALSE ? TEXT84 : TEXT85), &pat[0], TRUE)) != TRUE)
  927. /*                           "Replace" */
  928. /*                                    "Query replace" */
  929.         return(status);
  930.  
  931.     /* Ask for the replacement string.
  932.      */
  933.     if ((status = readpattern(TEXT86, &rpat[0], FALSE)) == ABORT)
  934. /*                                "with" */
  935.         return(status);
  936.  
  937.     /* Find the length of the replacement string.
  938.      */
  939.     rlength = strlen(&rpat[0]);
  940.  
  941.     /* Set up flags so we can make sure not to do a recursive
  942.      * replace on the last line.
  943.      */
  944.     nlflag = (pat[matchlen - 1] == '\r');
  945.     nlrepl = FALSE;
  946.  
  947.     if (kind)
  948.     {
  949.         /* Build query replace question string.
  950.          */
  951.         strcpy(tpat, TEXT87);
  952. /*                           "Replace '" */
  953.         expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  954.         strcat(tpat, TEXT88);
  955. /*                           "' with '" */
  956.         expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  957.         strcat(tpat, "'? ");
  958.  
  959.         /* Initialize last replaced pointers.
  960.          */
  961.         lastline = NULL;
  962.         lastoff = 0;
  963.     }
  964.  
  965.     /* Save original . position, init the number of matches and
  966.      * substitutions, and scan through the file.
  967.      */
  968.     origline = curwp->w_dotp;
  969.     origoff = curwp->w_doto;
  970.     numsub = 0;
  971.     nummatch = 0;
  972.  
  973.     while ( (f == FALSE || n > nummatch) &&
  974.         (nlflag == FALSE || nlrepl == FALSE) )
  975.     {
  976.         /* Search for the pattern.
  977.          * If we search with a regular expression,
  978.          * matchlen is reset to the true length of
  979.          * the matched string.
  980.          */
  981. #if    MAGIC
  982.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  983.         {
  984.             if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
  985.                 break;
  986.         }
  987.         else
  988. #endif
  989.             if (!scanner(&pat[0], FORWARD, PTBEG))
  990.                 break;        /* all done */
  991.  
  992.         ++nummatch;    /* Increment # of matches */
  993.  
  994.         /* Check if we are on the last line.
  995.          */
  996.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  997.  
  998.         /* Check for query.
  999.          */
  1000.         if (kind)
  1001.         {
  1002.             /* Get the query.
  1003.              */
  1004. pprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  1005. qprompt:
  1006.             update(TRUE);  /* show the proposed place to change */
  1007.             c = tgetc();            /* and input */
  1008.             mlwrite("");            /* and clear it */
  1009.  
  1010.             /* And respond appropriately.
  1011.              */
  1012.             switch (c)
  1013.             {
  1014. #if    FRENCH
  1015.                 case 'o':    /* yes, substitute */
  1016.                 case 'O':
  1017. #endif
  1018.                 case 'y':    /* yes, substitute */
  1019.                 case 'Y':
  1020.                 case ' ':
  1021.                     break;
  1022.  
  1023.                 case 'n':    /* no, onword */
  1024.                 case 'N':
  1025.                     forwchar(FALSE, 1);
  1026.                     continue;
  1027.  
  1028.                 case '!':    /* yes/stop asking */
  1029.                     kind = FALSE;
  1030.                     break;
  1031.  
  1032.                 case 'u':    /* undo last and re-prompt */
  1033.                 case 'U':
  1034.                     /* Restore old position.
  1035.                      */
  1036.                     if (lastline == NULL)
  1037.                     {
  1038.                         /* There is nothing to undo.
  1039.                          */
  1040.                         TTbeep();
  1041.                         goto pprompt;
  1042.                     }
  1043.                     curwp->w_dotp = lastline;
  1044.                     curwp->w_doto = lastoff;
  1045.                     lastline = NULL;
  1046.                     lastoff = 0;
  1047.  
  1048.                     /* Delete the new string.
  1049.                      */
  1050.                     backchar(FALSE, rlength);
  1051.                     status = delins(rlength, patmatch, FALSE);
  1052.                     if (status != TRUE)
  1053.                         return(status);
  1054.  
  1055.                     /* Record one less substitution,
  1056.                      * backup, save our place, and
  1057.                      * reprompt.
  1058.                      */
  1059.                     --numsub;
  1060.                     backchar(FALSE, mlenold);
  1061.                     matchline = curwp->w_dotp;
  1062.                     matchoff  = curwp->w_doto;
  1063.                     goto pprompt;
  1064.  
  1065.                 case '.':    /* abort! and return */
  1066.                     /* restore old position */
  1067.                     curwp->w_dotp = origline;
  1068.                     curwp->w_doto = origoff;
  1069.                     curwp->w_flag |= WFMOVE;
  1070.  
  1071.                 case BELL:    /* abort! and stay */
  1072.                     mlwrite(TEXT89);
  1073. /*                                              "Aborted!" */
  1074.                     return(FALSE);
  1075.  
  1076.                 default:    /* bitch and beep */
  1077.                     TTbeep();
  1078.  
  1079.                 case '?':    /* help me */
  1080.                     mlwrite(TEXT90);
  1081. /*"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "*/
  1082.                     goto qprompt;
  1083.  
  1084.             }    /* end of switch */
  1085.         }    /* end of "if kind" */
  1086.  
  1087.         /* if this is the point origin, flag so we a can reset it */
  1088.         if (curwp->w_dotp == origline) {
  1089.             origline = NULL;
  1090.             lastline = curwp->w_dotp->l_bp;
  1091.         }
  1092.  
  1093.         /*
  1094.          * Delete the sucker, and insert its
  1095.          * replacement.
  1096.          */
  1097.         status = delins(matchlen, &rpat[0], TRUE);
  1098.         if (origline == NULL) {
  1099.             origline = lastline->l_fp;
  1100.             origoff = 0;
  1101.         }
  1102.  
  1103.         if (status != TRUE)
  1104.             return(status);
  1105.  
  1106.         /* Save our position, since we may undo this.
  1107.          * If we are not querying, check to make sure
  1108.          * that we didn't replace an empty string
  1109.          * (possible in MAGIC mode), because we'll
  1110.          * infinite loop.
  1111.          */
  1112.         if (kind)
  1113.         {
  1114.             lastline = curwp->w_dotp;
  1115.             lastoff = curwp->w_doto;
  1116.         }
  1117.         else if (matchlen == 0)
  1118.         {
  1119.             mlwrite(TEXT91);
  1120. /*                              "Empty string replaced, stopping." */
  1121.             return(FALSE);
  1122.         }
  1123.  
  1124.         numsub++;    /* increment # of substitutions */
  1125.     }
  1126.  
  1127.     /* And report the results.
  1128.      */
  1129.     mlwrite(TEXT92, numsub);
  1130. /*              "%d substitutions" */
  1131.     return(TRUE);
  1132. }
  1133.  
  1134. /*
  1135.  * delins -- Delete a specified length from the current point
  1136.  *    then either insert the string directly, or make use of
  1137.  *    replacement meta-array.
  1138.  */
  1139. PASCAL NEAR delins(dlength, instr, use_meta)
  1140. int    dlength;
  1141. char    *instr;
  1142. int    use_meta;
  1143. {
  1144.     int    status;
  1145. #if    MAGIC
  1146.     RMC    *rmcptr;
  1147. #endif
  1148.  
  1149.     /* Zap what we gotta,
  1150.      * and insert its replacement.
  1151.      */
  1152.     if ((status = ldelete((long) dlength, FALSE)) != TRUE)
  1153.         mlwrite(TEXT93);
  1154. /*                      "%%ERROR while deleting" */
  1155.     else
  1156. #if    MAGIC
  1157.         if ((rmagical && use_meta) &&
  1158.              (curwp->w_bufp->b_mode & MDMAGIC) != 0)
  1159.         {
  1160.             rmcptr = &rmcpat[0];
  1161.             while (rmcptr->mc_type != MCNIL && status == TRUE)
  1162.             {
  1163.                 if (rmcptr->mc_type == LITCHAR)
  1164.                     status = linstr(rmcptr->rstr);
  1165.                 else
  1166.                     status = linstr(patmatch);
  1167.                 rmcptr++;
  1168.             }
  1169.         }
  1170.         else
  1171. #endif
  1172.             status = linstr(instr);
  1173.  
  1174.     return(status);
  1175. }
  1176.  
  1177. /*
  1178.  * expandp -- Expand control key sequences for output.
  1179.  */
  1180. PASCAL NEAR expandp(srcstr, deststr, maxlength)
  1181. char    *srcstr;    /* string to expand */
  1182. char    *deststr;    /* destination of expanded string */
  1183. int    maxlength;    /* maximum chars in destination */
  1184. {
  1185.     unsigned char c;    /* current char to translate */
  1186.  
  1187.     /* Scan through the string.
  1188.      */
  1189.     while ((c = *srcstr++) != 0)
  1190.     {
  1191.         if (c == '\r')        /* it's a newline */
  1192.         {
  1193.             *deststr++ = '<';
  1194.             *deststr++ = 'N';
  1195.             *deststr++ = 'L';
  1196.             *deststr++ = '>';
  1197.             maxlength -= 4;
  1198.         }
  1199.         else if (c < 0x20 || c == 0x7f)    /* control character */
  1200.         {
  1201.             *deststr++ = '^';
  1202.             *deststr++ = c ^ 0x40;
  1203.             maxlength -= 2;
  1204.         }
  1205.         else if (c == '%')
  1206.         {
  1207.             *deststr++ = '%';
  1208.             *deststr++ = '%';
  1209.             maxlength -= 2;
  1210.         }
  1211.         else            /* any other character */
  1212.         {
  1213.             *deststr++ = c;
  1214.             maxlength--;
  1215.         }
  1216.  
  1217.         /* check for maxlength */
  1218.         if (maxlength < 4)
  1219.         {
  1220.             *deststr++ = '$';
  1221.             *deststr = '\0';
  1222.             return(FALSE);
  1223.         }
  1224.     }
  1225.     *deststr = '\0';
  1226.     return(TRUE);
  1227. }
  1228.  
  1229. /*
  1230.  * boundry -- Return information depending on whether we may search no
  1231.  *    further.  Beginning of file and end of file are the obvious
  1232.  *    cases, but we may want to add further optional boundry restrictions
  1233.  *    in future, a' la VMS EDT.  At the moment, just return TRUE or
  1234.  *    FALSE depending on if a boundry is hit (ouch).
  1235.  */
  1236. int PASCAL NEAR boundry(curline, curoff, dir)
  1237. LINE    *curline;
  1238. int    curoff, dir;
  1239. {
  1240.     register int    border;
  1241.  
  1242.     if (dir == FORWARD)
  1243.     {
  1244.         border = (curoff == llength(curline)) &&
  1245.              (lforw(curline) == curbp->b_linep);
  1246.     }
  1247.     else
  1248.     {
  1249.         border = (curoff == 0) &&
  1250.              (lback(curline) == curbp->b_linep);
  1251.     }
  1252.     return(border);
  1253. }
  1254.  
  1255. /*
  1256.  * nextch -- retrieve the next/previous character in the buffer,
  1257.  *    and advance/retreat the point.
  1258.  *    The order in which this is done is significant, and depends
  1259.  *    upon the direction of the search.  Forward searches look at
  1260.  *    the current character and move, reverse searches move and
  1261.  *    look at the character.
  1262.  */
  1263. int PASCAL NEAR nextch(pcurline, pcuroff, dir)
  1264. LINE    **pcurline;
  1265. int    *pcuroff;
  1266. int    dir;
  1267. {
  1268.     register LINE    *curline;
  1269.     register int    curoff;
  1270.     register int    c;
  1271.  
  1272.     curline = *pcurline;
  1273.     curoff = *pcuroff;
  1274.  
  1275.     if (dir == FORWARD)
  1276.     {
  1277.         if (curoff == llength(curline))        /* if at EOL */
  1278.         {
  1279.             curline = lforw(curline);    /* skip to next line */
  1280.             curoff = 0;
  1281.             c = '\r';            /* and return a <NL> */
  1282.         }
  1283.         else
  1284.             c = lgetc(curline, curoff++);    /* get the char */
  1285.     }
  1286.     else            /* Reverse.*/
  1287.     {
  1288.         if (curoff == 0)
  1289.         {
  1290.             curline = lback(curline);
  1291.             curoff = llength(curline);
  1292.             c = '\r';
  1293.         }
  1294.         else
  1295.             c = lgetc(curline, --curoff);
  1296.  
  1297.     }
  1298.     *pcurline = curline;
  1299.     *pcuroff = curoff;
  1300.  
  1301.     return(c);
  1302. }
  1303.  
  1304. #if    MAGIC
  1305. /*
  1306.  * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  1307.  *    a literal character when (1) it is the first character in the
  1308.  *    pattern, and (2) when preceded by a symbol that does not allow
  1309.  *    closure, such as a newline, beginning of line symbol, or another
  1310.  *    closure symbol.
  1311.  *
  1312.  *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  1313.  *    speaking, unnecessary.  But right now we are so cramped for
  1314.  *    code space that i will grab what i can in order to remain
  1315.  *    within the 64K limit.  C compilers actually do very little
  1316.  *    in the way of optimizing - they expect you to do that.
  1317.  */
  1318. int PASCAL NEAR mcstr()
  1319. {
  1320.     MC    *mcptr, *rtpcm;
  1321.     char    *patptr;
  1322.      int    mj;
  1323.      int    pchr;
  1324.      int    status = TRUE;
  1325.      int    does_closure = FALSE;
  1326.  
  1327.     /* If we had metacharacters in the MC array previously,
  1328.      * free up any bitmaps that may have been allocated, and
  1329.      * reset magical.
  1330.      */
  1331.     if (magical)
  1332.         mcclear();
  1333.  
  1334.     mj = 0;
  1335.     mcptr = &mcpat[0];
  1336.     patptr = &pat[0];
  1337.  
  1338.     while ((pchr = *patptr) && status)
  1339.     {
  1340.         switch (pchr)
  1341.         {
  1342.             case MC_CCL:
  1343.                 status = cclmake(&patptr, mcptr);
  1344.                 magical = TRUE;
  1345.                 does_closure = TRUE;
  1346.                 break;
  1347.             case MC_BOL:
  1348.                 if (mj != 0)
  1349.                     goto litcase;
  1350.  
  1351.                 mcptr->mc_type = BOL;
  1352.                 magical = TRUE;
  1353.                 break;
  1354.             case MC_EOL:
  1355.                 if (*(patptr + 1) != '\0')
  1356.                     goto litcase;
  1357.  
  1358.                 mcptr->mc_type = EOL;
  1359.                 magical = TRUE;
  1360.                 break;
  1361.             case MC_ANY:
  1362.                 mcptr->mc_type = ANY;
  1363.                 magical = TRUE;
  1364.                 does_closure = TRUE;
  1365.                 break;
  1366.             case MC_CLOSURE:
  1367.                 /* Does the closure symbol mean closure here?
  1368.                  * If so, back up to the previous element
  1369.                  * and indicate it is enclosed.
  1370.                  */
  1371.                 if (!does_closure)
  1372.                     goto litcase;
  1373.                 mj--;
  1374.                 mcptr--;
  1375.                 mcptr->mc_type |= CLOSURE;
  1376.                 magical = TRUE;
  1377.                 does_closure = FALSE;
  1378.                 break;
  1379.  
  1380.             /* Note: no break between MC_ESC case and the default.
  1381.              */
  1382.             case MC_ESC:
  1383.                 if (*(patptr + 1) != '\0')
  1384.                 {
  1385.                     pchr = *++patptr;
  1386.                     magical = TRUE;
  1387.                 }
  1388.             default:
  1389. litcase:            mcptr->mc_type = LITCHAR;
  1390.                 mcptr->u.lchar = pchr;
  1391.                 does_closure = (pchr != '\r');
  1392.                 break;
  1393.         }        /* End of switch.*/
  1394.         mcptr++;
  1395.         patptr++;
  1396.         mj++;
  1397.     }        /* End of while.*/
  1398.  
  1399.     /* Close off the meta-string.
  1400.      */
  1401.     mcptr->mc_type = MCNIL;
  1402.  
  1403.     /* Set up the reverse array, if the status is good.  Please note the
  1404.      * structure assignment - your compiler may not like that.
  1405.      * If the status is not good, nil out the meta-pattern.
  1406.      * The only way the status would be bad is from the cclmake()
  1407.      * routine, and the bitmap for that member is guarenteed to be
  1408.      * freed.  So we stomp a MCNIL value there, and call mcclear()
  1409.      * to free any other bitmaps.
  1410.      */
  1411.     if (status)
  1412.     {
  1413.         rtpcm = &tapcm[0];
  1414.         while (--mj >= 0)
  1415.         {
  1416. #if    LATTICE
  1417.             movmem(--mcptr, rtpcm++, sizeof (MC));
  1418. #else
  1419.             *rtpcm++ = *--mcptr;
  1420. #endif
  1421.         }
  1422.         rtpcm->mc_type = MCNIL;
  1423.     }
  1424.     else
  1425.     {
  1426.         (--mcptr)->mc_type = MCNIL;
  1427.         mcclear();
  1428.     }
  1429.  
  1430.     return(status);
  1431. }
  1432.  
  1433. /*
  1434.  * rmcstr -- Set up the replacement 'magic' array.  Note that if there
  1435.  *    are no meta-characters encountered in the replacement string,
  1436.  *    the array is never actually created - we will just use the
  1437.  *    character array rpat[] as the replacement string.
  1438.  */
  1439. PASCAL NEAR rmcstr()
  1440. {
  1441.     RMC    *rmcptr;
  1442.     char    *patptr;
  1443.     int    status = TRUE;
  1444.     int    mj;
  1445.  
  1446.     patptr = &rpat[0];
  1447.     rmcptr = &rmcpat[0];
  1448.     mj = 0;
  1449.     rmagical = FALSE;
  1450.  
  1451.     while (*patptr && status == TRUE)
  1452.     {
  1453.         switch (*patptr)
  1454.         {
  1455.             case MC_DITTO:
  1456.  
  1457.                 /* If there were non-magical characters
  1458.                  * in the string before reaching this
  1459.                  * character, plunk it in the replacement
  1460.                  * array before processing the current
  1461.                  * meta-character.
  1462.                  */
  1463.                 if (mj != 0)
  1464.                 {
  1465.                     rmcptr->mc_type = LITCHAR;
  1466.                     if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1467.                     {
  1468.                         mlwrite(TEXT94);
  1469. /*                                                      "%%Out of memory" */
  1470.                         status = FALSE;
  1471.                         break;
  1472.                     }
  1473.                     bytecopy(rmcptr->rstr, patptr - mj, mj);
  1474.                     rmcptr++;
  1475.                     mj = 0;
  1476.                 }
  1477.                 rmcptr->mc_type = DITTO;
  1478.                 rmcptr++;
  1479.                 rmagical = TRUE;
  1480.                 break;
  1481.  
  1482.             case MC_ESC:
  1483.                 rmcptr->mc_type = LITCHAR;
  1484.  
  1485.                 /* We malloc mj plus two here, instead
  1486.                  * of one, because we have to count the
  1487.                  * current character.
  1488.                  */
  1489.                 if ((rmcptr->rstr = malloc(mj + 2)) == NULL)
  1490.                 {
  1491.                     mlwrite(TEXT94);
  1492. /*                                              "%%Out of memory" */
  1493.                     status = FALSE;
  1494.                     break;
  1495.                 }
  1496.  
  1497.                 bytecopy(rmcptr->rstr, patptr - mj, mj + 1);
  1498.  
  1499.                 /* If MC_ESC is not the last character
  1500.                  * in the string, find out what it is
  1501.                  * escaping, and overwrite the last
  1502.                  * character with it.
  1503.                  */
  1504.                 if (*(patptr + 1) != '\0')
  1505.                     *((rmcptr->rstr) + mj) = *++patptr;
  1506.  
  1507.                 rmcptr++;
  1508.                 mj = 0;
  1509.                 rmagical = TRUE;
  1510.                 break;
  1511.  
  1512.             default:
  1513.                 mj++;
  1514.         }
  1515.         patptr++;
  1516.     }
  1517.  
  1518.     if (rmagical && mj > 0)
  1519.     {
  1520.         rmcptr->mc_type = LITCHAR;
  1521.         if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1522.         {
  1523.             mlwrite(TEXT94);
  1524. /*                              "%%Out of memory" */
  1525.             status = FALSE;
  1526.         }
  1527.         bytecopy(rmcptr->rstr, patptr - mj, mj);
  1528.         rmcptr++;
  1529.     }
  1530.  
  1531.     rmcptr->mc_type = MCNIL;
  1532. }
  1533.  
  1534. /*
  1535.  * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
  1536.  */
  1537. PASCAL NEAR mcclear()
  1538. {
  1539.     register MC    *mcptr;
  1540.  
  1541.     mcptr = &mcpat[0];
  1542.  
  1543.     while (mcptr->mc_type != MCNIL)
  1544.     {
  1545.         if ((mcptr->mc_type & MASKCL) == CCL ||
  1546.             (mcptr->mc_type & MASKCL) == NCCL)
  1547.             free(mcptr->u.cclmap);
  1548.         mcptr++;
  1549.     }
  1550.     mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1551.     magical = FALSE;
  1552. }
  1553.  
  1554. /*
  1555.  * rmcclear -- Free up any strings, and MCNIL the RMC array.
  1556.  */
  1557. PASCAL NEAR rmcclear()
  1558. {
  1559.     register RMC    *rmcptr;
  1560.  
  1561.     rmcptr = &rmcpat[0];
  1562.  
  1563.     while (rmcptr->mc_type != MCNIL)
  1564.     {
  1565.         if (rmcptr->mc_type == LITCHAR)
  1566.             free(rmcptr->rstr);
  1567.         rmcptr++;
  1568.     }
  1569.  
  1570.     rmcpat[0].mc_type = MCNIL;
  1571. }
  1572.  
  1573. /*
  1574.  * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1575.  *    Software Tools, this is the function omatch(), but i felt there
  1576.  *    were too many functions with the 'match' name already.
  1577.  */
  1578. int PASCAL NEAR    mceq(bc, mt)
  1579. int    bc;
  1580. MC    *mt;
  1581. {
  1582.     register int result;
  1583.  
  1584.     switch (mt->mc_type & MASKCL)
  1585.     {
  1586.         case LITCHAR:
  1587.             result = eq(bc, (int) mt->u.lchar);
  1588.             break;
  1589.  
  1590.         case ANY:
  1591.             result = (bc != '\r');
  1592.             break;
  1593.  
  1594.         case CCL:
  1595.             if (!(result = biteq(bc, mt->u.cclmap)))
  1596.             {
  1597.                 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1598.                     (isletter(bc)))
  1599.                 {
  1600.                     result = biteq(CHCASE(bc), mt->u.cclmap);
  1601.                 }
  1602.             }
  1603.             break;
  1604.  
  1605.         case NCCL:
  1606.             result = !biteq(bc, mt->u.cclmap);
  1607.  
  1608.             if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1609.                 (isletter(bc)))
  1610.             {
  1611.                 result &= !biteq(CHCASE(bc), mt->u.cclmap);
  1612.             }
  1613.             break;
  1614.  
  1615.         default:
  1616.             mlwrite(TEXT95, mt->mc_type);
  1617. /*                              "%%mceq: what is %d?" */
  1618.             result = FALSE;
  1619.             break;
  1620.  
  1621.     }    /* End of switch.*/
  1622.  
  1623.     return(result);
  1624. }
  1625.  
  1626. /*
  1627.  * cclmake -- create the bitmap for the character class.
  1628.  *    ppatptr is left pointing to the end-of-character-class character,
  1629.  *    so that a loop may automatically increment with safety.
  1630.  */
  1631. int PASCAL NEAR    cclmake(ppatptr, mcptr)
  1632. char    **ppatptr;
  1633. MC    *mcptr;
  1634. {
  1635.     BITMAP        bmap;
  1636.     register char    *patptr;
  1637.     register int    pchr, ochr;
  1638.  
  1639.     if ((bmap = clearbits()) == NULL)
  1640.     {
  1641.         mlwrite(TEXT94);
  1642. /*                      "%%Out of memory" */
  1643.         return FALSE;
  1644.     }
  1645.  
  1646.     mcptr->u.cclmap = bmap;
  1647.     patptr = *ppatptr;
  1648.  
  1649.     /*
  1650.      * Test the initial character(s) in ccl for
  1651.      * special cases - negate ccl, or an end ccl
  1652.      * character as a first character.  Anything
  1653.      * else gets set in the bitmap.
  1654.      */
  1655.     if (*++patptr == MC_NCCL)
  1656.     {
  1657.         patptr++;
  1658.         mcptr->mc_type = NCCL;
  1659.     }
  1660.     else
  1661.         mcptr->mc_type = CCL;
  1662.  
  1663.     if ((ochr = *patptr) == MC_ECCL)
  1664.     {
  1665.         mlwrite(TEXT96);
  1666. /*                      "%%No characters in character class" */
  1667.         free(bmap);
  1668.         return(FALSE);
  1669.     }
  1670.     else
  1671.     {
  1672.         if (ochr == MC_ESC)
  1673.             ochr = *++patptr;
  1674.  
  1675.         setbit(ochr, bmap);
  1676.         patptr++;
  1677.     }
  1678.  
  1679.     while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
  1680.     {
  1681.         switch (pchr)
  1682.         {
  1683.             /* Range character loses its meaning
  1684.              * if it is the last character in
  1685.              * the class.
  1686.              */
  1687.             case MC_RCCL:
  1688.                 if (*(patptr + 1) == MC_ECCL)
  1689.                     setbit(pchr, bmap);
  1690.                 else
  1691.                 {
  1692.                     pchr = *++patptr;
  1693.                     while (++ochr <= pchr)
  1694.                         setbit(ochr, bmap);
  1695.                 }
  1696.                 break;
  1697.  
  1698.             /* Note: no break between case MC_ESC and the default.
  1699.              */
  1700.             case MC_ESC:
  1701.                 pchr = *++patptr;
  1702.             default:
  1703.                 setbit(pchr, bmap);
  1704.                 break;
  1705.         }
  1706.         patptr++;
  1707.         ochr = pchr;
  1708.     }
  1709.  
  1710.     *ppatptr = patptr;
  1711.  
  1712.     if (ochr == '\0')
  1713.     {
  1714.         mlwrite(TEXT97);
  1715. /*                      "%%Character class not ended" */
  1716.         free(bmap);
  1717.         return FALSE;
  1718.     }
  1719.     return TRUE;
  1720. }
  1721.  
  1722. /*
  1723.  * biteq -- is the character in the bitmap?
  1724.  */
  1725. int PASCAL NEAR    biteq(bc, cclmap)
  1726. int    bc;
  1727. BITMAP    cclmap;
  1728. {
  1729.     if (bc >= HICHAR)
  1730.         return FALSE;
  1731.  
  1732.     return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1733. }
  1734.  
  1735. /*
  1736.  * clearbits -- Allocate and zero out a CCL bitmap.
  1737.  */
  1738. BITMAP PASCAL NEAR clearbits()
  1739. {
  1740.     BITMAP        cclstart, cclmap;
  1741.     register int    j;
  1742.  
  1743.     if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
  1744.         for (j = 0; j < (HIBYTE); j++)
  1745.             *cclmap++ = 0;
  1746.  
  1747.     return(cclstart);
  1748. }
  1749.  
  1750. /*
  1751.  * setbit -- Set a bit (ON only) in the bitmap.
  1752.  */
  1753. PASCAL NEAR setbit(bc, cclmap)
  1754. int    bc;
  1755. BITMAP    cclmap;
  1756. {
  1757.     if (bc < HICHAR)
  1758.         *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1759. }
  1760. #endif
  1761.